#include"stdafx.h"
#include "bochs.h"


const bx_bool bx_parity_lookup[256] = {
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
};

void IA32_CPU::handleAsyncEvent(void)
{

	if ( debug_trap & 0x80000000) 
	{  //HLT
		debug_trap = 0; // clear traps for after resume
		inhibit_mask = 0; // clear inhibits for after resume
	} 

	if ( debug_trap & 0x00008000) 
	{
		dr6 |=  debug_trap;
		exception(IA32_DB_EXCEPTION, 0, 0); // no error, not interrupt
	}


	if (debug_trap && !( inhibit_mask & IA32_INHIBIT_DEBUG) )
	{
		dr6 |=  debug_trap;
		exception(IA32_DB_EXCEPTION, 0, 0); // no error, not interrupt
	}

	if ( get_TF ())
	{
		debug_trap |= 0x00004000; // BS flag in DR6
	}


	if ( get_RF ()) 
	{
		clear_RF ();
	}
	else 
	{
		if (  dr7 & 0x000000ff ) 
		{
			Bit32u iaddr =  get_segment_base(IA32_SEG_REG_CS) +  prev_eip;
			Bit32u dr6_bits;
			if ( (dr6_bits = hwdebug_compare(iaddr, 1, IA32_HWDebugInstruction, IA32_HWDebugInstruction)) )
			{
				// Add to the list of debug events thus far.
				async_event = 1;
				debug_trap |= dr6_bits;
				if ( !( inhibit_mask & IA32_INHIBIT_DEBUG) ) 
				{
					dr6 =  debug_trap;
					exception(IA32_DB_EXCEPTION, 0, 0); // no error, not interrupt
				}
			}
		}
	}

	inhibit_mask = 0;
	if ( !( debug_trap ||  get_TF ()  || ( dr7 & 0xff) ))
		async_event = 0;

	return; // Continue executing cpu_loop.
}



void IA32_CPU::prefetch(void)
{
	//ia32_address laddr;
	//ia32_address temp_rip;
	//ia32_address laddrPageOffset0, eipPageOffset0;

	//temp_rip   = EIP;
	//Bit32u limit = sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled;
	//laddr =  get_segment_base(IA32_SEG_REG_CS) + temp_rip;

	//if (((Bit32u)temp_rip) > limit) 
	//{
	//	exception(IA32_GP_EXCEPTION, 0, 0);
	//}

	//laddrPageOffset0 = laddr & 0xfffff000;
	//eipPageOffset0 = EIP - (laddr - laddrPageOffset0);
	//eipPageBias = 0 - eipPageOffset0;
	//eipPageWindowSize = DEFAULT_PAGESIZE; // FIXME:
	////////////////////////////////////////////////////////////////////////////
	//if(limit + eipPageBias < 4096) {
	//	eipPageWindowSize = (Bit32u)(limit + eipPageBias + 1);
	//}
	//////////////////////////////////////////////////////////////////////////
	//eipFetchPtr = m_vm32->load_code_page(this, laddr, CPL==3 );
}

void IA32_CPU::boundaryFetch(Bit8u *fetchPtr, unsigned remainingInPage, Ia32_Instruction_c *i)
{
//	unsigned j;
//	Bit8u fetchBuffer[16]; // Really only need 15
//	unsigned ret;
//
//	if (remainingInPage >= 15) 
//	{
//		exception(IA32_GP_EXCEPTION, 0, 0);
//	}
//
//	for (j=0; j<remainingInPage; j++) 
//	{
//		fetchBuffer[j] = *fetchPtr++;
//	}
//
//	EIP += remainingInPage;
//	prefetch();
//
//	fetchPtr =  eipFetchPtr;
//	for (; j<15; j++) 
//	{
//		fetchBuffer[j] = *fetchPtr++;
//	}
//	ret = fetchDecode(fetchBuffer, i, 15);
//	if (ret==0) 
//	{
//		exception(IA32_GP_EXCEPTION, 0, 0);
//	}
//
//	EIP =  prev_eip;
//	eipPageWindowSize = 0; // Fixme
//
}

void IA32_CPU::fetchInstruction( Bit32u addr, Ia32_Instruction_c * i )
{
	Bit8u data[16] ;
	size_t eff = 0;
	m_vm32->access_address_n( this, addr, sizeof(data), 3, IA32_EXEC|IA32_READ, data, &eff );
	if( !eff )
	{
		exception(IA32_PF_EXCEPTION, 0, 0);
	}
	if( !fetchDecode( data, i, eff ) )
	{
		exception(IA32_GP_EXCEPTION, 0, 0);
	}
}

// return true means repeat completed
bool IA32_CPU::repeat( Ia32_Instruction_c * i , size_t times )
{
	unsigned _repUsedValue = i->repUsedValue();
	bx_bool zf_repeat = (i->repeatableZFL()!=0) && (_repUsedValue==2||_repUsedValue==3);
	
	if( zf_repeat ) 
	{
		bx_bool wantZF = (_repUsedValue==2) ? 1 : 0;
		if (i->as32L()) 
		{
			for( size_t k = 0; k < times; ++ k )
			{
				if( get_ZF() == wantZF ) return true;
				if( ECX==0 ) return true;
				IA32_CPU_CALL_METHOD(i->execute, (i));
				ECX --;
			}
		}
		else
		{
			for( size_t k = 0; k < times; ++ k )
			{
				if( get_ZF() == wantZF ) return true;
				if( ECX==0 ) return true;
				IA32_CPU_CALL_METHOD(i->execute, (i));
				CX --;
			}
		}
	} 
	else 
	{
		if (i->as32L()) 
		{
			for( size_t k = 0; k < times; ++ k )
			{
				if( ECX==0 ) return true;
				IA32_CPU_CALL_METHOD(i->execute, (i));
				ECX--;
			}
		} 
		else 
		{
			for( size_t k = 0; (CX) && (k < times); ++ k )
			{
				if( CX ==0 ) return true;
				IA32_CPU_CALL_METHOD(i->execute, (i));
				CX --;
			}
		}
	}
	return false;
}

size_t IA32_CPU::cpu_loop( size_t steps, size_t check_bits )
{
	if( check_bits ) return cpu_loop_with_check( steps, check_bits );
	return cpu_loop_no_check(steps);
}

size_t IA32_CPU::cpu_loop_with_check( size_t steps, size_t check_bits )
{
	size_t executed = 0;
	StopReason.klass = ClassNothing;

	setjmp(jmp_buf_env);

	if( StopReason.klass ) 
		return executed;

	for( ; executed < steps; ++ executed )
	{
		TickCount ++;
		if( m_current_in_repeat )
		{
			m_current_in_repeat = repeat( &m_current_instruction, 0x1000 ) == false;
			continue;
		}

		if( check_bits & CheckOnInstruction )
			m_vm32->on_condition_check( this, CheckOnInstruction );

		prev_eip = EIP; // commit new EIP
		prev_esp = ESP;

		// check on events which occurred for previous instructions (traps)
		// and ones which are asynchronous to the CPU (hardware interrupts)
		if( async_event ) 
			handleAsyncEvent();

		//////////////////////////////////////////////////////////////////////////
		{
			Ia32_Instruction_c * i = m_vm32->alloc_instruction_in_cache(this,EIP);
			if( !i->ilen() )
			{
				if( check_bits & CheckOnFetch )
					m_vm32->on_condition_check( this, CheckOnFetch );

				fetchInstruction( EIP, i );
				m_vm32->on_instruction_fetched( this, EIP );
			}
			m_current_instruction = *i;
		}
		//////////////////////////////////////////////////////////////////////////

		BxExecutePtr_tR resolveModrm = m_current_instruction.ResolveModrm; // Get as soon as possible for speculation
		if (resolveModrm) 
			IA32_CPU_CALL_METHODR(resolveModrm, (&m_current_instruction));

		bx_bool is_repeat = m_current_instruction.repUsedL();
		
		is_repeat = is_repeat ? (m_current_instruction.repeatableL() || m_current_instruction.repeatableZFL() ) : false;

		EIP += m_current_instruction.ilen();

		size_t next_eip = EIP;

		if( m_current_in_repeat )
		{
			m_current_in_repeat = repeat( &m_current_instruction, 0x1000 ) == false;
		}
		else
		{
			//printf( "EIP = %08X\n", prev_eip );
			//if( prev_eip == 0x01013015 ) __asm int 3;
			IA32_CPU_CALL_METHOD(m_current_instruction.execute, (&m_current_instruction) );
		}

		if( (EIP != next_eip) && (check_bits & CheckOnJumped) )
		{
			m_vm32->on_condition_check( this, CheckOnJumped );
		}

	} 
	return executed;
}


size_t IA32_CPU::cpu_loop_no_check( size_t steps )
{
	size_t executed = 0;
	StopReason.klass = ClassNothing;

	setjmp(jmp_buf_env);

	if( StopReason.klass ) 
		return executed;

	// We get here either by a normal function call, or by a longjmp
	// back from an exception() call.  In either case, commit the
	// new EIP/ESP, and set up other environmental fields.  This code
	// mirrors similar code below, after the interrupt() call.

	for( ; executed < steps; ++ executed )
	{
		TickCount ++;
		if( m_current_in_repeat )
		{
			m_current_in_repeat = repeat( &m_current_instruction, 0x1000 ) == false;
			continue;
		}
		
		//printf( "EIP -> EIP : %08X -> %08X\n", prev_eip, EIP );
		//if( 0X01017052 == EIP ) __asm int 3;
		prev_eip = EIP; // commit new EIP
		prev_esp = ESP;

		// check on events which occurred for previous instructions (traps)
		// and ones which are asynchronous to the CPU (hardware interrupts)
		if( async_event )
			handleAsyncEvent();

		//////////////////////////////////////////////////////////////////////////
		{
			Ia32_Instruction_c * i = m_vm32->alloc_instruction_in_cache(this,EIP);
			if( !i->ilen() )
			{
				fetchInstruction( EIP, i );
				m_vm32->on_instruction_fetched( this, EIP );
			}
			m_current_instruction = *i;
		}
		//////////////////////////////////////////////////////////////////////////

		BxExecutePtr_tR resolveModrm = m_current_instruction.ResolveModrm; // Get as soon as possible for speculation
		if (resolveModrm) 
			IA32_CPU_CALL_METHODR(resolveModrm, (&m_current_instruction));

		bx_bool is_repeat = m_current_instruction.repUsedL();

		is_repeat = is_repeat ? (m_current_instruction.repeatableL() || m_current_instruction.repeatableZFL() ) : false;

		EIP += m_current_instruction.ilen();

		if( m_current_in_repeat )
		{
			m_current_in_repeat = repeat( &m_current_instruction, 0x1000 ) != true;
		}
		else
		{
			//printf( "EIP = %08X\n", prev_eip );
			IA32_CPU_CALL_METHOD(m_current_instruction.execute, (&m_current_instruction) );
		}
	} 
	return executed;
}